﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using Gwn.Library.MvpVm.Entities;
using Gwn.Library.MvpVm.Events;
using Gwn.Module.UserManager.Interfaces;
using Gwn.Module.UserManager.Types;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Unity;

namespace Gwn.Module.UserManager.Layers
{
    public class DataAccessLayer : IDataAccessLayer
    {
        /// <summary>
        /// Gets or sets the event aggregator.
        /// </summary>
        /// <value>The event aggregator.</value>
        [Dependency]
        public IEventAggregator EventAggregator { get; set; }

        private readonly ObservableCollection<User> _mockUserData;

        /// <summary>
        /// Initializes a new instance of the <see cref="DataAccessLayer"/> class.
        /// </summary>
        public DataAccessLayer()
        {
            // Use mock data upon initialization
            _mockUserData = GetData();
        }

        /// <summary>
        /// Lists the data.
        /// </summary>
        public void ListData()
        {
            var results = _mockUserData;
            var args = new ProcessEventArgs
                           {
                               Data = results,
                               ProcessType =  UserManagerProcessType.ListData,
                               FriendlyMessage = results.Count + " records retrieved"
                           };

            // Publish the list
            EventAggregator.GetEvent<ProcessEvent>().Publish(args);
        }


        /// <summary>
        /// Creates the specified sender.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        public void Create(object sender, EventArgs e)
        {
            var args = e as ProcessEventArgs;

            // Get the user record and update its primary key
            var data = args.GetData<User>();
            data.Id = GetUniqueId();

            // Add the new record to the existing mock data
            _mockUserData.Add(data);

            // Update args
            args.ProcessType = UserManagerProcessType.CreateData;
            args.FriendlyMessage = "Create successful!";

            // Publish the update
            PublishRecord(args);
        }

        /// <summary>
        /// Reads the specified sender.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        public void Read(object sender, EventArgs e)
        {
            var args = e as ProcessEventArgs;

            // Find the users id in the actual list and get
            // a fresh copy of it.
            var data = _mockUserData.FirstOrDefault(p => p.Id == args.GetData<User>().Id);

            args.Data = data;
            args.ProcessType = UserManagerProcessType.ReadData;
            args.FriendlyMessage = "Read successful!";

            // Publish the read
            PublishRecord(args);
        }

        /// <summary>
        /// Updates the specified sender.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        public void Update(object sender, EventArgs e)
        {
            var args = e as ProcessEventArgs;

            var newRecord = args.GetData<User>();

            // Get a reference to our data
            var data = _mockUserData.FirstOrDefault(p => p.Id == newRecord.Id);

            data.FirstName = newRecord.FirstName;
            data.LastName = newRecord.LastName;
            data.MiddleName = newRecord.MiddleName;
            data.DateOfBirth = newRecord.DateOfBirth;

            // Update args 
            args.Data = data;
            args.ProcessType = UserManagerProcessType.UpdateData;
            args.FriendlyMessage = "Update successful!";

            // Publish
            PublishRecord(args);
        }

        /// <summary>
        /// Deletes the specified sender.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        public void Delete(object sender, EventArgs e)
        {
            var args = e as ProcessEventArgs;
            var newRecord = args.GetData<User>();

            // Get a reference to our data
            var data = _mockUserData.FirstOrDefault(p => p.Id == newRecord.Id);

            // Update our event arguments
            args.FriendlyMessage = "Delete successful!";
            args.ProcessType = UserManagerProcessType.DeleteData;

            // Remove from our data source (list)
            _mockUserData.Remove(data);

            // Publish the status
            PublishRecord(args);
        }


        /// <summary>
        /// Publishes the record.
        /// </summary>
        /// <param name="processType">Type of the process.</param>
        /// <param name="record">The record.</param>
        private void PublishRecord(ProcessEventArgs e)
        {
            EventAggregator.GetEvent<ProcessEvent>().Publish(e);
        }

        private DateTime GetDate(int years)
        {
            return DateTime.Now;
        }

        private int GetUniqueId()
        {
            if (_mockUserData.Count == 0)
                return 1;

            var results = _mockUserData.Max(p => p.Id);
            return results + 1;
        }

        private ObservableCollection<User> GetData()
        {
            var results = new ObservableCollection<User>
                       {
                           new User{ Id=1, FirstName = "Bill",LastName = "Kratochvil",DateOfBirth = GetDate(20),MiddleName = "NA"},
                           new User {Id=2, FirstName = "John", LastName = "Doe", MiddleName = "", DateOfBirth = GetDate(30)},
                           new User {Id=3, FirstName = "Jane", LastName = "Doe", MiddleName = "", DateOfBirth = GetDate(29)}
                       };
            return results;
        }
    }
}
